/****************************************************
	文件：RPrefab.cs
	作者：HuskyT
	邮箱：1005240602@qq.com
	日期：2021/1/4 13:36:29
	功能：Prefab 加载器
*****************************************************/

using System;
using System.Collections.Generic;
using HTFW.Core;
using HTFW.Logger;
using UnityEngine;
using IDisposable = HTFW.Core.IDisposable;
using Object = UnityEngine.Object;

namespace HTFW.Res
{
    public class RPrefab : IInitializable, IDisposable
    {
        /// <summary>
        /// 实例化的 GameObject 与其对应资源之间的映射
        /// </summary>
        Dictionary<GameObject, ResData> mMap;

        HashSet<string> mPreloadSet;

        IContext mResContext;

        public RPrefab(IContext context)
        {
            mResContext = context;
            mResContext.OnInit += (resContext) => Init();
            mResContext.OnDispose += (resContext) => Dispose();
        }

        public void Init(params object[] args)
        {
            mMap = new Dictionary<GameObject, ResData>();
            mPreloadSet = new HashSet<string>();
        }

        public void Dispose()
        {
            ClearAll();
            mMap = null;
            mPreloadSet = null;
        }

        /// <summary>
        /// GameObject 唯一的实例化接口
        /// </summary>
        /// <param name="path"></param>
        /// <param name="parent"></param>
        /// <returns></returns>
        public GameObject Instantiate(string path, Transform parent = null)
        {
            ResData assetRes = ResService.Instance.LoadSync(path);

            if (mPreloadSet.Contains(path))
            {
                assetRes.DecreaseRef();
                mPreloadSet.Remove(path);
            }

            GameObject obj = Instantiate(assetRes.Obj as GameObject, parent);
            mMap.Add(obj, assetRes);
            return obj;
        }

        /// <summary>
        /// 预加载到内存（同步）
        /// </summary>
        /// <param name="path"></param>
        public void PreloadSync(string path)
        {
            if (ResCache.Instance.Contains(path)) return;
            ResService.Instance.LoadSync(path);
            mPreloadSet.Add(path);
        }

        /// <summary>
        /// 预加载到内存（异步）
        /// </summary>
        /// <param name="path"></param>
        /// <param name="priority"></param>
        /// <param name="onCompleted"></param>
        public void PreloadAsync(string path, ResTaskPriority priority, Action<Object> onCompleted)
        {
            if (ResCache.Instance.Contains(path)) return;
            ResService.Instance.LoadAsync(path, priority, (assetRes) =>
            {
                mPreloadSet.Add(path);
                onCompleted?.Invoke(assetRes.Obj);
            });
        }

        /// <summary>
        /// GameObject 唯一的销毁接口
        /// </summary>
        /// <param name="obj"></param>
        /// <param name="clearMemory"></param>
        public void Destroy(GameObject obj, bool clearMemory = false)
        {
            if (!mMap.TryGetValue(obj, out ResData assetRes))
            {
                HLog.Error($"Destroy error! 重复销毁 or 未使用统一接口实例化 GameObject：{obj.name}");
                return;
            }

            mMap.Remove(obj);
            GameObject.Destroy(obj);
            assetRes.IsClearMemory = clearMemory;
            ResService.Instance.Unload(assetRes);
        }

        /// <summary>
        /// 清除所有预加载但还未实例化的资源
        /// </summary>
        public void ClearPreload()
        {
            foreach (var path in mPreloadSet)
            {
                if (ResCache.Instance.TryGet(path, out ResData assetRes))
                {
                    assetRes.IsClearMemory = true;
                    ResService.Instance.Unload(assetRes);
                }
            }

            mPreloadSet.Clear();
        }

        /// <summary>
        /// 清除实例化资源（防止引用丢失导致内存泄漏）
        /// </summary>
        public void ClearInstance(bool clearMemory)
        {
            List<GameObject> tempLst = new List<GameObject>();
            foreach (var item in mMap)
            {
                if (System.Object.ReferenceEquals(item.Key, null)) continue;
                tempLst.Add(item.Key);
            }

            foreach (var obj in tempLst)
                Destroy(obj, clearMemory);
        }

        /// <summary>
        /// 清除所有资源内存
        /// </summary>
        public void ClearAll()
        {
            ClearPreload();
            ClearInstance(true);
        }

        GameObject Instantiate(GameObject prefab, Transform parent)
        {
            if (prefab == null) return null;

            GameObject obj = GameObject.Instantiate(prefab);

            if (parent != null) obj.transform.SetParent(parent, false);

            obj.transform.localPosition = prefab.transform.localPosition;
            obj.transform.localRotation = prefab.transform.localRotation;
            obj.transform.localScale = prefab.transform.localScale;

            return obj;
        }
    }
}